#include "XSystem/XCore.h"

XResourcesManager::XResourcesManager()
{
	
}

// Loads the config file and put the variables in maps.
void XResourcesManager::Init(sf::RenderWindow* app)
{
	x_dataDir = "../../data/";
	x_app = app;
	x_displayLogs = false;

	// Read the config file.
	NewLog("Opening 'config.txt' file...");
	std::ifstream confFile(x_dataDir+"config.txt");
	if (confFile)
	{
		std::string line;
		size_t space;

		// While there is still variables to read, insert them in the map.
		while (getline(confFile, line))
		{
			char datatype = line[0];
			space = line.find(" ");

			std::string dataname = line.substr(1, space-1);
			std::string datavalue = line.substr(space+1, line.size()-space-1);

			if (datatype == 'S')
			{
				x_sdata[dataname] = datavalue;
				x_csdata.push_back(dataname);

			} else {

				int datavaluei = atoi(datavalue.c_str());
				x_idata[dataname] = datavaluei;
				x_cidata.push_back(dataname);
			}
		}

		confFile.close();

	} else {

		// CANNOT OPEN FILE
		NewLog("ERROR: Cannot open 'config.txt'");
	}

	NewLog("Starting logger...");
	std::stringstream ss;
	ss << "Running in " << x_idata["resolutionX"] << "x" << x_idata["resolutionX"] << " resolution.";
	NewLog(ss.str());

	// Load the font
	if (!x_font.LoadFromFile(x_dataDir+"font.ttf", 8))
        NewLog("ERROR: Cannot open 'font.ttf'");

	// Network
	x_idata["isConnected"] = 0;
}

// Exit the resources manager; save the config file and the log file.
void XResourcesManager::Close()
{
	// Save the config file.
	NewLog("Saving 'config.txt' file...");
	std::ofstream confFile(x_dataDir+"config.txt");
	if (confFile)
	{
		for (int i=0; i<int(x_cidata.size()); i++)
			confFile << "I" << x_cidata[i] << " " << x_idata[x_cidata[i]] << std::endl;

		for (int i=0; i<int(x_csdata.size()); i++)
			confFile << "S" << x_csdata[i] << " " << x_sdata[x_csdata[i]] << std::endl;

		confFile.close();

	} else {

		// CANNOT SAVE FILE
		NewLog("ERROR: Cannot open 'config.txt'");
	}

	// Save the log file.
	NewLog("Saving 'logs.txt' file...");
	std::ofstream logFile("logs.txt");
	if (logFile)
	{
		for (int i=0; i<int(x_logs.size()); i++)
			logFile << x_logs[i] << std::endl;
	}

	// Disconnect from the server, if connected
	if (x_idata["isConnected"] == 1)
	{
		struct functions packetInformation;
		packetInformation.function = "disconnect";
		packetInformation.argument = "";
		packetInformation.variable = "";

		x_qdata["packets"].push(packetInformation);
	}
}

// Send the data asked for (int). If it doesn't exist, set it to zero.
// \return the data(int) asked for
const int& XResourcesManager::GetInt(const std::string& dataname)
{
	// Check, whether the data already exists
	for(std::map<std::string, int>::const_iterator it = x_idata.begin(); it != x_idata.end(); ++it)
	{
		if(dataname == it->first)
		{
			return it->second;
		}
	}
	
	// The data doesen't exists. Create it and save it.
	int temp = 0;
	x_idata[dataname] = temp;
	return x_idata[dataname];
}

// Send the data asked for (string). If it doesn't exist, set it to an empty string.
// \return the data(string) asked for
const std::string& XResourcesManager::GetString(const std::string& dataname)
{
	// Check, whether the data already exists
	for(std::map<std::string, std::string>::const_iterator it = x_sdata.begin(); it != x_sdata.end(); ++it)
	{
		if(dataname == it->first)
		{
			return it->second;
		}
	}
	
	// The data doesen't exists. Create it and save it.
	std::string temp = "";
	x_sdata[dataname] = temp;
	return x_sdata[dataname];
}

// Set the data (int) for the value.
void XResourcesManager::SetInt(const std::string& dataname, int value)
{
	x_idata[dataname] = value;
}

// Set the data (string) for the value.
void XResourcesManager::SetString(const std::string& dataname, std::string value)
{
	x_sdata[dataname] = value;
}

// /return if the queue is empty or not
bool XResourcesManager::queueEmpty(std::string queue)
{
	int size = x_qdata[queue].size();
	if (size == 0)
		return true;

	return false;
}

// Get the next function, then delete it
const struct functions XResourcesManager::nextQueue(std::string queue)
{
	struct functions function = x_qdata[queue].front();
	x_qdata[queue].pop();

	return function;
}

// Put a new function in a queue
void XResourcesManager::NewQueue(std::string queue, std::string argument, std::string variable)
{
	struct functions function;
	function.argument = argument;
	function.variable = variable;

	x_qdata[queue].push(function);
}

// Look if an image already exist. If it does, send the pointer. If not, load it then send it.
// \return the image asked for
const sf::Image& XResourcesManager::GetImage(const std::string& filename)
{
	// Check, whether the image already exists
	for(std::map<std::string, sf::Image>::const_iterator it = x_images.begin(); it != x_images.end(); ++it)
	{
		if(filename == it->first)
		{
			return it->second;
		}
	}
	
	// The image doesen't exists. Create it and save it.
	sf::Image image;
	if(image.LoadFromFile(x_dataDir+filename))
	{
		image.SetSmooth(false);
		image.CreateMaskFromColor(sf::Color(255, 0, 255));
		image.CreateMaskFromColor(sf::Color(100, 100, 100), 128);

		x_images[filename] = image;
		return x_images[filename];
	}

	// CANNOT LOAD IMAGE
	NewLog("ERROR: Cannot load '"+filename+"'");
	x_images[filename] = image;
	return x_images[filename];
}

// \return a pointer to the sf::RenderWindow app.
sf::RenderWindow* XResourcesManager::GetApp()
{
	return x_app;
}

// Add a new entry in the log vector. Display it if set to. (false by default)
void XResourcesManager::NewLog(std::string log)
{
	x_logs.push_back(log);

	if (x_displayLogs)
		std::cout << log << std::endl;
}

// Set if the logs are displayed or not. If set to display the logs, display the old logs before.
void XResourcesManager::DisplayLogs(bool displayLogs)
{
	x_displayLogs = displayLogs;

	// Display the old logs.
	if (x_displayLogs)
		for (int i=0; i<int(x_logs.size()); i++)
			std::cout << x_logs[i] << std::endl;
}

// \return a reference to the sf::Font font.
sf::Font& XResourcesManager::GetFont()
{
	return x_font;
}

// Look if any packets was received
void XResourcesManager::ReceivePackets()
{
	if (x_idata["isConnected"] == 1)
	{
		int characterID;
		std::string function;
		std::string argument;
		std::string variable;

		sf::Packet packet;
		x_socket.Receive(packet);

		if (!(packet >> characterID >> function >> argument >> variable))
			NewLog("ERROR: Couldn't receive packet.");
		else
		{
			// If it's a variable(s)
			if (function == "")
			{
				std::string currentVariableName;
				std::string currentVariable;
				int currentVariableInt;
				int lastCheck = 0;
				int checkPosition = 0;
				bool done = false;
				bool isString = true;

				while (lastCheck < variable.size()-1)
				{
					// Check for the variable name
					done = false;
					while (!done)
					{
						if (variable[checkPosition] == '=')
						{
							currentVariableName = variable.substr(lastCheck,  (checkPosition - lastCheck));
							done = true;
							lastCheck = checkPosition + 1;
						}

						checkPosition++;
					}

					// Check for the variable value
					done = false;
					while (!done)
					{
						if (variable[checkPosition] == ';')
						{
							currentVariable = variable.substr(lastCheck,  (checkPosition - lastCheck));
							done = true;
							lastCheck = checkPosition + 1;

							// Check if it's an integer
							isString = true;
							char numbers[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
							for (int i=0; i<10; i++)
							{
								if (currentVariable[0] == numbers[i])
								{
									currentVariableInt = atoi(currentVariable.c_str());
									isString = false;
								}
							}
						}

						checkPosition++;
					}

					// Update the value
					if (isString)
						SetString(currentVariableName, currentVariable);
					else
						SetInt(currentVariableName, currentVariableInt);

					std::cout << "New variable value '" << currentVariableName << "' with value '" << currentVariable << "' string:" << isString << std::endl;
				}

			// If it's a function
			} else {

				NewQueue(function, argument, variable);

				std::cout << "New function with value '" << argument << "' variable '" << variable << "'" << std::endl;
			}
		}
	}
}

// Send packets
void XResourcesManager::SendPackets()
{
	if (x_qdata["packets"].size() != 0)
	{
		struct functions packetInformation = x_qdata["packets"].front();
		x_qdata["packets"].pop();

		sf::Packet packet;

		packet << x_idata["client-id"] << packetInformation.function << packetInformation.argument << packetInformation.variable;

		std::cout << "Sending packet : " << packetInformation.function << ";" << packetInformation.argument << ";" << packetInformation.variable << std::endl;

		x_socket.Send(packet);
	}
}

// Send a new packet
void XResourcesManager::NewPacket(std::string function, std::string argument, std::string variable)
{
	struct functions packetInformation;
	packetInformation.function = function;
	packetInformation.argument = argument;
	packetInformation.variable = variable;

	x_qdata["packets"].push(packetInformation);
}

// Connects to the server
bool XResourcesManager::Connect()
{
	NewLog("Attempt to connect to the server...");

	sf::IPAddress IPAddress(x_sdata["server-ip"]);
    if (x_socket.Connect(x_idata["tcp-port"], IPAddress) == sf::Socket::Done)
    {
		NewLog("Done!");
		x_idata["isConnected"] = 1;
		return true;

	} else {

		NewLog("Could not connect.");
		return false;
	}
}
